package com.xixilala.video.practices

import android.Manifest
import android.content.Context
import android.content.pm.PackageManager
import android.media.*
import android.media.AudioFormat.ENCODING_PCM_16BIT
import android.net.Uri
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.core.app.ActivityCompat
import com.xixilala.video.App
import com.xixilala.video.base.BaseFragment
import com.xixilala.video.databinding.FragmentPractice2Binding
import com.xixilala.video.util.ExecutorServer
import com.xixilala.video.util.FileUtil.pcmFile
import com.xixilala.video.util.FileUtil.wavFile
import com.xixilala.video.util.LogUtil
import com.xixilala.video.util.wav.WaveHeader
import java.io.FileInputStream
import java.io.FileOutputStream

/**
 * 使用 AudioRecord 和 AudioTrack API 完成音频 PCM 数据的采集和播放，并实现读写音频 wav 文件
 */
class Practice2Fragment : BaseFragment() {

    private lateinit var mBinding: FragmentPractice2Binding

    private val sampleSize = 44100
    private val channel = AudioFormat.CHANNEL_IN_DEFAULT
    private val encoding = ENCODING_PCM_16BIT
    private val minBufferSize = AudioRecord.getMinBufferSize(sampleSize, channel, encoding)

    private val audioSessionId = (App.appContext.getSystemService(Context.AUDIO_SERVICE) as AudioManager).generateAudioSessionId()

    private var recordState: RecordState = RecordState.UNINIT

    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
//        val requestPermissionContract: ActivityResultContracts.RequestPermission = ActivityResultContracts.RequestPermission()
//        val registerForActivityResult = registerForActivityResult(requestPermissionContract) { }
//        registerForActivityResult.launch(Manifest.permission.RECORD_AUDIO)
        mBinding = FragmentPractice2Binding.inflate(inflater, container, false)
        return mBinding.root
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)

        mBinding.btnAudioRecordSave.setOnClickListener {
            startRecord()
        }

        mBinding.btnAudioRecordSavePause.setOnClickListener {
            if (recordState == RecordState.PAUSE) {
                recordState = RecordState.RECORDING
            } else if (recordState == RecordState.RECORDING) {
                recordState = RecordState.PAUSE
            }
        }

        mBinding.btnAudioRecordSaveStop.setOnClickListener {
            recordState = RecordState.STOP
        }

        mBinding.btnAudioRecordPlay.setOnClickListener {

//            ExecutorServer.submit(Runnable {  })

            ExecutorServer.submit {
                playPcm()
            }
        }

        mBinding.btnAudioRecordSave2wav.setOnClickListener {
            ExecutorServer.submit {
                saveFile2Wav()
            }
        }
        mBinding.btnAudioRecordPlayWav.setOnClickListener {
            playWav()
        }
    }

    private fun startRecord() {

        if (ActivityCompat.checkSelfPermission(mActivity, Manifest.permission.RECORD_AUDIO) != PackageManager.PERMISSION_GRANTED) {
            return
        }
        val audioRecord = AudioRecord(MediaRecorder.AudioSource.DEFAULT, sampleSize, channel, encoding, minBufferSize)
        recordState = RecordState.INIT

        ExecutorServer.submit {

            saveAudio2File(audioRecord)

        }

        audioRecord.startRecording()
        recordState = RecordState.RECORDING

    }

    private fun saveAudio2File(audioRecord: AudioRecord) {
        val data = ByteArray(minBufferSize)
        val file = pcmFile(true)
        val fos = FileOutputStream(file)

        audioRecord.startRecording()
        recordState = RecordState.RECORDING

        while (true) {
            LogUtil.d("recording state = $recordState")
            if (recordState == RecordState.RECORDING) {
                audioRecord.read(data, 0, minBufferSize)
                fos.write(data)
            } else if (recordState == RecordState.PAUSE) {

            } else if (recordState == RecordState.STOP) {
                fos.flush()
                fos.close()
                break
            }
        }
    }

    private fun playPcm() {

        val audioTrackAttributes = AudioAttributes.Builder()
            .setLegacyStreamType(AudioManager.STREAM_MUSIC)
            .build()
        val audioFormat = AudioFormat.Builder()
            .setEncoding(encoding)
            .setChannelMask(channel)
            .setSampleRate(sampleSize)
            .build()
        val audioTracker = AudioTrack(audioTrackAttributes, audioFormat, minBufferSize, AudioTrack.MODE_STREAM, audioSessionId)

//        val audioTracker = AudioTrack(AudioManager.STREAM_MUSIC, sampleSize, channel, encoding, minBufferSize, AudioTrack.MODE_STREAM)


        val fis = FileInputStream(pcmFile())
        val data = ByteArray(minBufferSize)
        var length: Int
        while (run {
                length = fis.read(data)
                length
            } > 0) {
            audioTracker.play()
            audioTracker.write(data, 0, length)
        }
        audioTracker.stop()
        audioTracker.release()

        fis.close()
    }

    private fun saveFile2Wav() {
        val fis = FileInputStream(pcmFile())
        val fos = FileOutputStream(wavFile(true))

        val buf = ByteArray(1024 * 4)
        var size = fis.read(buf)
        var pcmSize = 0
        while (size != -1) {
            pcmSize += size
            size = fis.read(buf)
        }
        fis.close()

        val wavHeader = WaveHeader().apply {
            fileLength = pcmSize + (44 - 8)
            FmtHdrLeth = 16
            BitsPerSample = 16
            Channels = 1
            FormatTag = 0x0001
            SamplesPerSec = sampleSize
            BlockAlign = (Channels * BitsPerSample / 8).toShort()
            AvgBytesPerSec = BlockAlign * SamplesPerSec
            DataHdrLeth = pcmSize
        }
        val h = wavHeader.header

        assert(h.size==44)

        fos.write(h, 0, h.size)

        val fis1 = FileInputStream(pcmFile())

        size = fis1.read(buf)
        while (size != -1) {
            fos.write(buf, 0, size)
            size = fis1.read(buf)
        }
        fos.flush()
        fis1.close()
        fos.close()
    }

    private fun playWav() {
        val mediaPlayer = MediaPlayer.create(mActivity, Uri.parse(wavFile().absolutePath))
        mediaPlayer?.start()
    }

    enum class RecordState {
        UNINIT, INIT, RECORDING, PAUSE, STOP
    }
}